package com.kpatil.erp.core.measure;

import java.util.ArrayList;
import java.util.List;

public class UnitConverter {
	public static final double convert(double quantity, UnitOfMeasure input,
			UnitOfMeasure target) {
		if (input == null || target == null
				|| input.getType() != target.getType()) {
			throw new IllegalArgumentException("Can not covert from unit '"
					+ input + "' to unit '" + target + "'");
		} else if (quantity == 0) {
			return 0;
		} else {
			final Result result = new Result(quantity, input);
			while (result.getUnit() != null
					&& result.getUnit() instanceof DerivedUnit) {
				final DerivedUnit derived = (DerivedUnit) result.getUnit();
				final UnitOfMeasure base = derived.getParent();
				final double ratio = derived.getRatio();
				final double converted = result.getQuantity() * ratio;
				if (target.equals(base)) {
					return converted;
				}
				result.setQuantity(converted);
				result.setUnit(base);
			}

			final List<DerivedUnit> toCollection = collectTillBase(target);
			for (int iCounter = toCollection.size() - 1; iCounter >= 0; iCounter--) {
				final DerivedUnit derived = toCollection.get(iCounter);
				result.setQuantity(result.getQuantity() / derived.getRatio());
				result.setUnit(derived);
			}

			return result.getQuantity();
		}
	}

	private static List<DerivedUnit> collectTillBase(final UnitOfMeasure target) {
		final List<DerivedUnit> result = new ArrayList<DerivedUnit>();
		UnitOfMeasure current = target;
		while (current != null && current instanceof DerivedUnit) {
			final DerivedUnit derived = (DerivedUnit) current;
			current = derived.getParent();
			result.add(derived);
		}
		return result;
	}

	private static class Result {
		private double quantity;
		private UnitOfMeasure unit;

		public Result(final double quantity, UnitOfMeasure uom) {
			this.quantity = quantity;
			this.unit = uom;
		}

		public double getQuantity() {
			return quantity;
		}

		public UnitOfMeasure getUnit() {
			return unit;
		}

		public void setQuantity(double quantity) {
			this.quantity = quantity;
		}

		public void setUnit(UnitOfMeasure unit) {
			this.unit = unit;
		}
	}

	public static void main(String[] args) {
		final BaseUnit kg = new BaseUnit("kg", UnitType.MASS, 1.000000d,
				0.00010d);
		final DerivedUnit gram = new DerivedUnit("g", kg, 0.001d);
		final DerivedUnit milligram1 = new DerivedUnit("mg", gram, 0.001d);
		final DerivedUnit milligram2 = new DerivedUnit("mg1", kg, 0.000001d);
		
		System.out.println("500 grams into kg : " + convert(500, gram, kg));
		System.out.println("500 kg into gram : " + convert(500, kg, gram));
		System.out.println("500 kg into miligram : "
				+ convert(500, kg, milligram1));
		System.out.println("500 kg into miligram : "
				+ convert(500, kg, milligram2));
	}
}
